package comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
//import java.util.LinkedList;
//import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 客户端连接保持线程类
 * @author administrator
 *
 */
//IO node -io节点
class IONode extends Thread {
	boolean isDispCurrentLog = false; // debug用-本class常规消息是否打日志

	private boolean isEnd;
	final private Socket socket;

	// 常规信息
	private EInitState initState = EInitState.WAIT; // 初始化进度，由主循环线程读写，IO线程仅在构造函数对其进行初始化
	private String macAddress; // mac地址
	private int instrumentID = 0; // 仪器ID

	// 网络状态控制
	private NetState netState = new NetState();

	// 同步需求控制
	private UpdateState updateState = new UpdateState();

	// 待回复数据超时重发控制
	private final long TimeoutTimerDataMaxTime;// 数据重发超时时限（mS）
	private final int MaxResendTimes; // 超时重发次数上限
	private long TimeoutTimerDataEndTime = 0; // 预计的数据超时时间
	private int currentResendTimes = 0; // 超时重发当前次数

	// 数据流控制
	private final int socketReadTimeLimit = 100; // 读socket超时（ms）
	private InputStream in = null;
	private OutputStream out = null;

	// 数据缓存控制
	private RecvByteBuffer recvByteBuffer = new RecvByteBuffer(); // 接收字节缓存
	private boolean isWaitingReply = false;

	// LinkedList（不支持并发），ConcurrentLinkedQueue（支持并发）
	private ConcurrentLinkedQueue<DataSend> dataSendNoReply = new ConcurrentLinkedQueue<DataSend>(); // 正常情况下仅几条（心跳包，回复包原则上一个可能超时重发时几个？？）
	private ConcurrentLinkedQueue<DataSend> dataSendNeedReply = new ConcurrentLinkedQueue<DataSend>(); // 正常情况下很多条
	private ConcurrentLinkedQueue<DataRecv> dataRecv = new ConcurrentLinkedQueue<DataRecv>(); // 需要回复的包原则上一个或超时后重发的一两个
	private final int recvDataMaxNum = 10; // 接收池最大条数（须大于2），超过此值一半则不发送待回复消息，超过此值时丢弃新包
	// // ok消息（1），心跳包，接收消息（1），具体最大包个数由主循环处理周期与心跳包周期决定
	// 注：若主循环线程在一个IO待的时间超过类预定值，会形成恶性循环

	private Log log = new Log();

	static int instrumentIDToNodeNum(int aInstrumentID) {
		if (aInstrumentID >= 65536 && aInstrumentID < 65536 + 65536) {
			return aInstrumentID % 65536;
		} else {
			return -1;
		}
	}

	IONode(Socket socket) {
		this.socket = socket;
		this.isEnd = false;
		this.initState = EInitState.WAIT;

		int num = PropertyManager.getInt("TimeoutTimerDataMaxTime", 10);
		if (num < 0)
			num = 10;
		this.TimeoutTimerDataMaxTime = 1000 * (long) num; // 等待回复超时重发时间
		num = PropertyManager.getInt("MaxResendTimes", 3);
		if (num < 1 || num > 10)
			num = 3;
		this.MaxResendTimes = num; // 等待回复超时重发次数上限
	}

	int getInstrumentID() {
		return this.instrumentID;
	}

	void setInstrumentID(int aInstrumentID) {
		this.instrumentID = aInstrumentID;
	}

	String getMacAddr() {
		return this.macAddress;
	}

	void setMacAddr(String aMacAddr) {
		this.macAddress = aMacAddr;
	}

	Log getLog() {
		return this.log;
	}

	// 获取当前网络状态（是否适合同步）
	// // 网络状态好回true，否则返回false
	boolean getNetStateForUpdate() {
		return this.netState.getStateForUpdate();
	}

	// 获取最后一次心跳包中刷卡机状态
	int getLastState() {
		return this.netState.getLastState();
	}

	// 获取当前网络状态（是否在线，给web查询刷卡机网络状态用）
	// // 在线返回true，否则返回false
	boolean getNetStateForWeb() {
		return this.netState.getStateForWeb();
	}

	// 更新相关
	// 设置更新请求（参数：类型，请求时间）
	void setUpdateReq(EUpdateType aUpdateType, long aUpdateTime) {
		this.updateState.setUpdateReq(aUpdateType, aUpdateTime);
	}

	// 设置更新完成（参数：类型，完成时间）
	void setUpdateDone(EUpdateType aUpdateType, long aUpdateTime) {
		this.updateState.setUpdateDone(aUpdateType, aUpdateTime);
	}

	// 获取更新需求（某类型是否待更新）
	boolean getUpdateNeed(EUpdateType aUpdateType) {
		return this.updateState.getUpdateNeed(aUpdateType);
	}

	// 获取初始化进度
	EInitState getInitState() {
		return this.initState;
	}

	// 设置初始化进度
	void setInitState(EInitState aInitState) {
		if (null != aInitState) {
			this.initState = aInitState;
		}
	}

	// 获取接收池消息条数-由执行模块调用??安全性验证
	int getRecvDataCount() {
		// debug
		int size = dataRecv.size();
		if (size < 0) {
			log.print(Log.warn, "dataRecv.size()<0" + "instrumentID:" + this.instrumentID);
			dataRecv.clear();
			size = 0;
		}
		return size;
	}

	// 从接收池中取一条消息-由主循环线程中执行模块调-接收池空时返回null？？安全性验证
	DataRecv getRecvData() {
		// debug
		DataRecv newDataNode = dataRecv.peek();
		int size = dataRecv.size();
		if (size > 0 && newDataNode == null) {
			// ?? 刷卡机发送消息过快时可能跳到此处
			log.print(Log.warn, "instrumentID:" + this.instrumentID + "size > 0 && newDataNode == null" + " size:" + size + "dataRecv.size():" + dataRecv.size() + "dataRecv.peek():" + dataRecv.peek());
			dataRecv.clear();
		}

		return dataRecv.poll();
	}

	// 获取待回复发送池消息条数-由执行模块调用
	int getDataSendNeedReplyCount() {
		return dataSendNeedReply.size();
	}

	// 从待回复发送池取消息
	// // 用于检查是否有未更新成功到预约
	DataSend getDataSendNeedReply() {
		return this.dataSendNeedReply.poll();
	}

	// 添加一条数据到发送池-由主循环线程中执行模块调-发送池满时返回false
	boolean addData(final DataSend dataNode, final EReplyNeed isNeedReply) {
		if (null != dataNode) {
			if (EReplyNeed.NEEDREPLY == isNeedReply) {
				return dataSendNeedReply.offer(dataNode);
			} else {// isNeedReply 为EReplyNeed.NOREPLY或null
				return dataSendNoReply.offer(dataNode);
			}
		} else {
			return true; // 空消息，直接返回true
		}

	}

	void closeSocket() { // 关闭socket，执行模块强制关闭IO线程时调用
		try {
			if (false == this.socket.isClosed()) {
				this.socket.close();
			}
		} catch (IOException e) {
			Log.println(e.toString() + "instrumentID:" + this.instrumentID);
		}
	}

	// 节点线程是否已被终止
	// // 主循环线程通过此判断是否需要删除节点
	boolean isEnd() {
		return this.isEnd;
	}

	void end() { // 终止本线程
		//Log.println("end--" + Log.getTraceList());
		isEnd = true;
	}

	// IO节点线程--负责发数据，收数据，协议解析，处理心跳包，处理回复消息
	// isEnd变为true后即认为线程已经处于终止状态，此后新产生的与该节点相关的数据可能会丢失
	public void run() {
		String ioStr = "newIONode";
		try {
			// 初始化
			ioStr = socket.getInetAddress() + ":" + Integer.toString(socket.getPort());
			log.print(Log.info, "io线程启动，客户端:" + "仪器ID" + this.instrumentID + ioStr);

			in = socket.getInputStream();
			out = socket.getOutputStream();
			// 初始化socket读数据超时时间
			socket.setSoTimeout(socketReadTimeLimit);

			// 初始化IO超时定时器
			this.netState.start();

			// IO循环
			log.print(Log.info, "IO循环启动" + "仪器ID" + this.instrumentID);
			while (isEnd == false) {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					Log.println(e.toString() + "instrumentID:" + this.instrumentID);
				}
				recvData();
				netState.flush(); // 更新各个网络状态（for IO，web，Update）
				if (netState.isTimeOutForNet()) {
					log.print(Log.error, "数据包等待超时，退出" + "仪器ID" + this.instrumentID);
					Log.println("数据包等待超时" + "仪器ID" + this.instrumentID);
					break;
				}
				sendData();
				if (currentResendTimes > MaxResendTimes) { // 待回复消息超时重发次数过多
					log.print(Log.error, "超时重发次数过多，退出" + "仪器ID" + this.instrumentID);
					this.isEnd = true;
					break;
				}
			}
		} catch (Exception e) {
			Log.println(e);
			log.print(Log.error, e.toString() + " instrumentID:" + this.instrumentID);
		} finally {
			this.end();
			try {
				if (false == this.socket.isClosed()) {
					this.socket.close();
				}

			} catch (IOException e) {
				log.print(Log.warn, e.toString() + " instrumentID:" + this.instrumentID);
				Log.println(e.toString() + " instrumentID:" + this.instrumentID);
			}
			this.netState.stop();
			log.print(Log.info, "io线程终止，客户端:" + "仪器ID" + this.instrumentID + ioStr);
		}

	}

	// “数据超时定时器“清零，新的需要等待回复的消息发出后调用
	private void resetDataTimer() {
		this.TimeoutTimerDataEndTime = System.currentTimeMillis() + this.TimeoutTimerDataMaxTime;
	}

	// 检查“数据超时定时器“是否超时
	private boolean isDataTimeOut() {
		long currentTime = System.currentTimeMillis();
		if (true == this.isWaitingReply && currentTime > this.TimeoutTimerDataEndTime) {
			return true;
		} else {
			return false;
		}
	}

	// IO发数据-将发送池中当前可发的消息全部发出
	// // write可能阻塞（socket缓冲满时会阻塞）
	private void sendData() throws IOException {
		DataSend dataNode = null;
		// 发送不需要等待回复的数据（将当前发送队列中已有数据发出去）
		int dataNodeNum = this.dataSendNoReply.size();
		while ((dataNodeNum--) > 0 && null != this.dataSendNoReply.peek()) {
			dataNode = this.dataSendNoReply.poll();
			if (dataNode != null) {
				this.out.write(dataNode.encodeToByteArray());
				this.out.flush();
				if (true == this.isDispCurrentLog) {
					this.log.print(Log.debug, "发送（无回复）消息" + dataNode.getDataID() + ":" + dataNode.toString() + " instrumentID:" + this.instrumentID);
				}
			} else {// ??为什么会出现空
				this.log.print(Log.warn, "发送队列（不需要回复的消息）数据节点为null" + " instrumentID:" + this.instrumentID); // 有时候会跳过来？？
				this.dataSendNoReply.clear();
				break;
			}
		}
		// 发送需要等待回复的数据（每次只发一个包）
		// // 条件：接收缓存中没有大量等待主循环线程回复的消息，且有待发送的待回复消息
		if (this.dataRecv.size() < recvDataMaxNum / 2 && this.dataSendNeedReply.size() > 0) {
			if (false == this.isWaitingReply) { // 没有数据正在等待回复
				dataNode = dataSendNeedReply.peek();
				if (dataNode != null) {
					this.out.write(dataNode.encodeToByteArray());
					this.out.flush();
					if (true == this.isDispCurrentLog) {
						this.log.print(Log.debug, "发送（需回复）消息" + dataNode.getDataID() + ":" + dataNode.toString() + "仪器ID：" + this.instrumentID);
					}
					this.isWaitingReply = true;
					this.resetDataTimer();
				} else {
					this.dataSendNeedReply.poll();
					this.log.print(Log.warn, "发送队列（需回复）弹出空数据" + "仪器ID:" + this.instrumentID);
				}
			} else { // 有数据在等待回复
				if (true == this.isDataTimeOut()) { // 等待回复超时，重发当前数据
					this.currentResendTimes++;
					dataNode = this.dataSendNeedReply.peek();
					if (dataNode != null) {
						this.out.write(dataNode.encodeToByteArray());
						this.out.flush();
						this.log.print(Log.debug, "发送（需回复）消息-重发次数：" + currentResendTimes + " dataID：" + dataNode.getDataID() + ":" + dataNode.toString() + " instrumentID:" + this.instrumentID);
						this.resetDataTimer(); // 发出需要回复数据后，将数据超时定时器清零
					} else {
						this.dataSendNeedReply.poll();
						this.log.print(Log.warn, "发送队列（需回复）弹出空数据" + "仪器ID:" + this.instrumentID);
					}
				}
			}
		}
	}

	// IO收数据-读取socket缓存，转换为消息对象存储在接收池中
	// // 若收到心跳包，则将超时定时器清零
	// // 若为回复包，将相应数据超时等待标志清零，并弹出已发送成功到消息
	// // 若为无效回复包，直接丢弃，不清等待标志
	// // 注：若网络不好，服务器在刷卡机超时未回复情况下重发后收到刷卡机对同一包的两次回复，有可能会丢掉后边一个包（出现的可能性很小）
	// //
	// ？？接收消息过多问题：可能IO线程收到OK包后塞接收缓存后立即发送下一待回复包，若主循环线程处理速度慢于IO线程会造成接收消息积累（可能积累的很快）
	// //
	// ？？上述问题是否有必要把下一次发送待回复包触发交给主循环线程，暂时改为接收缓存中连续收到一半最大长度个数消息即不在发送待回复消息（防止发送待回复消息后收到回复而造成恶性循环）
	private void recvData() throws IOException {
		// 读socket缓存
		DataRecv dataNode = null;
		try {
			if (this.recvByteBuffer.getFreeSize() > 0) {
				int readNum = in.read(this.recvByteBuffer.buffer, this.recvByteBuffer.bufferTail, this.recvByteBuffer.getFreeSize());
				if (readNum > 0) {
					this.recvByteBuffer.bufferTail += readNum;
					readNum = 0;
				}
			} else {
				this.log.print(Log.warn, "接收缓存满" + " instrumentID:" + this.instrumentID);
			}
		} catch (SocketTimeoutException e) {
			// System.out.println("等待数据超时");
		}

		// 解析并处理消息（前边收到到数据有限，这里到while不是死循环）
		dataNode = this.recvByteBuffer.check(); // 包解析
		while (dataNode != null) {
			this.netState.resetStateOfNet(); // 收到正常包，更新网络状态
			// 消息响应
			int dataId = dataNode.getDataID();
			if (true == this.isDispCurrentLog) {
				this.log.print(Log.debug, "收到包:" + dataId + ":" + dataNode.toString() + "仪器ID：" + this.instrumentID); // debug
			}
			if (DataHeartBeatRecv.dataID == dataId) {
				// 心跳包（也要上传）
				DataHeartBeatRecv dataHeartBeatRecv = (DataHeartBeatRecv) dataNode;
				this.netState.resetStateOfHeartBeat(dataHeartBeatRecv.getMsgInstrumentState()); // 写入心跳包状态
				// Log.println("hearet state" +
				// dataHeartBeatRecv.getMsgInstrumentState());
				dataHeartBeatRecv = null;
				// Log.println("收到心跳包"); // debug
			} else if (dataId == DataCommandRecv.dataID) {
				// 通用消息包
				// log.print("iorecv", Log.debug, "收到通用消息包" + dataId);
				DataCommandRecv dataRecvCommand = (DataCommandRecv) dataNode;
				if (true == this.isWaitingReply && true == dataRecvCommand.isCommandReplyOK()) {
					// 收到回复包，回复包加入发送包
					// log.print("iorecv", Log.debug, "收到回复包");
					// 判断回复有效，清除回复等待标志，弹出已发送成功到请求
					if (false == this.isWaitingReply) {
						log.print(Log.debug, "没有等待回复包情况下收到ok" + "仪器ID：" + this.instrumentID);
					}
					this.isWaitingReply = false;
					dataRecvCommand.setDataSendOfReply(this.dataSendNeedReply.poll());
				}
				dataRecvCommand = null;
			} else {
				// 其他消息在IO线程中无操作
			}
			if (this.dataRecv.size() < recvDataMaxNum) {
				// 接收池消息没有超过上限
				// 插入接收池，由执行模块处理
				this.dataRecv.offer(dataNode);
			} else {
				log.print(Log.warn, "接收池消息过多，丢弃新消息" + " instrumentID:" + this.instrumentID);
				log.print(Log.warn, "接收池消息过多，丢弃新消息" + "消息数目上限：" + recvDataMaxNum + "当前消息数量" + this.dataRecv.size() + "当前回复包等待状态isWaitingReply:" + this.isWaitingReply + "仪器ID" + this.instrumentID);

				// debug
				// 正常运行需删掉（可能会造成同步问题（两个地方都或poll））
				for (int i = 0; i < recvDataMaxNum; i++) {
					DataRecv newData = this.dataRecv.poll();
					if(null == newData){
						Log.println("**得到空消息");
						continue;
					}
					log.print(Log.info, "i:" + i + "积攒的消息类型ID:" + newData.getDataID() + newData.toString() + " instrumentID:" + this.instrumentID);
					if (2 == newData.getDataID()) {
						log.print(Log.info, "CommandID:" + ((DataCommandRecv) newData).msg.getOption());
					}
				}
				break;
			}

			dataNode = recvByteBuffer.check(); // 继续包解析
		}
		dataNode = null;
		recvByteBuffer.clean(); // 数据压缩（把后边读数据移到起始处）
	}

	// 枚举类（待发送消息是否需要回复）
	// // 仅两个相反的值
	enum EReplyNeed {
		NEEDREPLY, NOREPLY
	}

	// 枚举类（初始化时使用：初始化完成情况）
	// // 0-WAIT等待状态
	// // 1-INIT正在初始化已获得仪器ID准备发送初始化信息列表
	// // 2-初始化列表已发送等待消息列表回复和初始化完成回复
	// // 3-初始化已完成（需保证仪器ID和mac地址已填入）
	enum EInitState {
		WAIT, TO_SEND_INIT_LIST, WAITING_REPLY, INITED
	}

	// 待更新状态维护类（IO线程内部类）
	private class UpdateState {
		private UpdateTime[] updateTimes;

		UpdateState() {
			this.updateTimes = new UpdateTime[EUpdateType.values().length];
			for (int i = 0; i < updateTimes.length; i++)
				updateTimes[i] = new UpdateTime();
		}

		// 标记待同步
		// 由webservice调用，mainloop在数据库一些类型数据更新后调用，涉及多个线程，有一定并发性
		// 20130508修改：将每次标记待同步实现由后一次标记覆盖前一次标记，改为后一次标记不能覆盖前一次标记
		//？？可能会有并发问题（会有多个线程调用）
		void setUpdateReq(final EUpdateType aType, final long aTime) {
			final int typeIndex = aType.getUpdateTypeArrayIndex();
			final long timeUpdateReq = this.updateTimes[typeIndex].timeUpdateReq;
			final long timeUpdateDone = this.updateTimes[typeIndex].timeUpdateDone;
			if (null != aType && timeUpdateReq < timeUpdateDone) {
				// 待更新类型有效，且未标记待同步
				this.updateTimes[aType.getUpdateTypeArrayIndex()].timeUpdateReq = aTime;
			}
		}

		void setUpdateDone(final EUpdateType aType, final long aTime) {
			if (null != aType) {
				this.updateTimes[aType.getUpdateTypeArrayIndex()].timeUpdateDone = aTime;
			}
		}

		// 判断某一类型更新是否待同步（待同步时间不早于已同步时间）
		boolean getUpdateNeed(final EUpdateType aType) {
			// 参数检查
			if (null == aType) {
				return false;
			}
			// 检查
			final int typeIndex = aType.getUpdateTypeArrayIndex();
			final long timeUpdateReq = this.updateTimes[typeIndex].timeUpdateReq;
			final long timeUpdateDone = this.updateTimes[typeIndex].timeUpdateDone;
			if (timeUpdateReq >= timeUpdateDone && timeUpdateReq + 1000 < System.currentTimeMillis()) {
				//Log.println("timeUpdateReq:" + timeUpdateReq + "timeUpdateDone" + timeUpdateDone);
				// 有新的同步请求（请求同步时间大于已完成同步时间），且距请求同步时间已超过1S（避免标记数据库同步无效，重复同步问题）
				// 未避免由于WEB连续请求同步造成同步迟迟不能执行，标记待同步时不允许重复标记待更新
				return true;
			} else {
				return false;
			}

		}

		private class UpdateTime { // 初始化 timeUpdateReq >
									// timeUpdateDone，自动触发首次更新
			long timeUpdateReq = 1;
			long timeUpdateDone = 0;
		}
	}

	// 网络状态维护类（IO线程内部类）
	// // 每次循环调flush刷新心跳状态若已超过心跳包等待时间则清空心跳包计数，每收到一个心跳包对心跳包计数加一
	// // ，连续收到一定数量心跳包不再加一，直接将心跳包状态置为好。可通过istimeout测试已经连续丢失到心跳包个数是否超过上限
	// // 若每次循环周期大于一个心跳包间隔时间会出现漏查心跳包的可能性（应避免）
	// // 每次触发同步（查数据库）都要在网络状态好时进行（即最近连续收到10个以上心跳包）
	// 总原则（举例）：连续及时收到5个心跳包认为刷卡机在线，连续及时收到10个心跳包主动发起同步，5秒钟没有收到任何正确包认为刷卡机离线，10秒钟没有收到任何正确包认为刷卡机故障关闭连接

	private class NetState {
		private boolean isRunning = false;

		final int MaxHeartBeatNum = 200; // 心跳包计数最大值（若超过此值不再增加）
		final int MaxHeartBeatNumForUpdate; // 连续收到10个心跳包，认为网络状态好，可以触发更新
		final int MaxHeartBeatNumForWeb; // 连续收到5个心跳包，将刷卡机网络状态为在线
		final long TimeOfHeartBeat; // ms 心跳包正常间隔时间
		final long MaxTimeOfHeartBeat; // ms，最大心跳包间隔时间，心跳包间隔时间超过此认为丢失心跳包
		final long MinTimeOfHeartBeat; // ms，最小心跳包间隔时间，心跳包间隔时间小于此认为心跳包无效，不参与心跳包计数
		final long MaxNoPackageTimeForNet; // ms 没有收到任何正确包的时间超过此值时关闭连接
		final long MaxNoPackageTimeForWeb; // ms 没有收到任何正确包的时间超过此值时刷卡机置为离线

		private int numOfHeartBeat = 0; // 连续正常收到心跳包次数
		private long lastHeartBeatTime = 0; // 最近一次收到心跳包时间
		private long lastPackageTime = 0; // 最近一次收到正确包时间
		private boolean stateForWeb = false;
		private int lastInstrumentState = 0; // 0表示离线，若收到心跳包则将状态更新到此变量

		NetState() {
			// 需要先读取心跳包时间（心跳包时间为其他一些时间参数的基准）
			int num = 0;
			// 心跳包时间正常
			num = PropertyManager.getInt("TimeOfHeartBeat", 1);
			if (num < 0)
				num = 1;
			this.TimeOfHeartBeat = 1000 * (long) num;
			// 心跳包时间上限
			num = PropertyManager.getInt("MaxTimeOfHeartBeat", 5);
			if (num < 0)
				num = 5;
			this.MaxTimeOfHeartBeat = this.TimeOfHeartBeat * (long) num;
			// 心跳包时间下限
			num = PropertyManager.getInt("MinTimeOfHeartBeat", 5);
			if (num < 0)
				num = 5;
			this.MinTimeOfHeartBeat = this.TimeOfHeartBeat / (long) num;
			// 网络状态好可触发同步条件（心跳包个数）
			num = PropertyManager.getInt("MaxHeartBeatNumForUpdate", 10);
			if (num < 0)
				num = 10;
			this.MaxHeartBeatNumForUpdate = num;
			// WEB端显示刷卡机在线条件（心跳包个数）
			num = PropertyManager.getInt("MaxHeartBeatNumForWeb", 5);
			if (num < 0)
				num = 5;
			this.MaxHeartBeatNumForWeb = num;
			// WEB端显示刷卡机离线条件（心跳包个数）
			num = PropertyManager.getInt("MaxNoPackageTimeForWeb", 5);
			if (num < 0)
				num = 5;
			this.MaxNoPackageTimeForWeb = this.MaxTimeOfHeartBeat * (long) num;
			// 离线上限时间，超过此时间关闭连接（心跳包个数）
			num = PropertyManager.getInt("MaxNoPackageTimeForNet", 10);
			if (num < 0)
				num = 10;
			this.MaxNoPackageTimeForNet = this.MaxTimeOfHeartBeat * (long) num;

			this.isRunning = false;
		}

		void start() {
			this.isRunning = true;
			this.resetStateOfNet();
		}

		void stop() {
			this.isRunning = false;
			this.stateForWeb = false;
		}

		void resetStateOfHeartBeat(final int aInstrumentState) { // 收到心跳包，重置心跳包状态值，更新状态
			this.flush();
			final long currentTime = System.currentTimeMillis();
			if (currentTime - this.lastHeartBeatTime < this.MinTimeOfHeartBeat) {
				// 两次收到心跳包时间差过短，不参与连续心跳包计数，且不更新心跳包时间，但更新刷卡机状态
				// // 连续收到多个心跳包不会马上进入网状状态好状态，刷卡机总是以很短时间间隔发心跳包时仍按设置的比价慢的速度拿有效到心跳包触发网络状态变更
				Log.println("**心跳包间隔时间过短（小于MinTimeOfHeartBeat:" + this.MinTimeOfHeartBeat + "ms）：currentTime" + currentTime + "this.lastHeartBeatTime" + this.lastHeartBeatTime + "instrumentID:" + IONode.this.instrumentID);
			} else {
				// 据上次有效心跳包间隔时间有效
				this.lastHeartBeatTime = currentTime;
				if (this.numOfHeartBeat < this.MaxHeartBeatNum) {
					this.numOfHeartBeat++;
				}
			}			
			this.lastInstrumentState = aInstrumentState;
			if (false == this.stateForWeb && this.numOfHeartBeat > this.MaxHeartBeatNumForWeb) {
				Log.println("刷卡机状态改为在线" + "仪器ID：" + IONode.this.instrumentID);
				this.stateForWeb = true; // 置刷卡及到web可见网络状态
			}
		}

		void resetStateOfNet() { // 收到任何包
			this.lastPackageTime = System.currentTimeMillis();
		}

		void flush() { // 应在IO线程收发数据的循环中调用（随时更新状态）
			// 若心跳包等待超时，置心跳包个数
			// 若正常包等待超时，置刷卡机到web状态
			long currentTime = System.currentTimeMillis();
			if (currentTime - this.lastHeartBeatTime > this.MaxTimeOfHeartBeat) {
				// 检查刷卡机的正常心跳包个数是否需清零
				this.numOfHeartBeat = 0;
			}
			if ((true == this.stateForWeb) && (currentTime - this.lastPackageTime > this.MaxNoPackageTimeForWeb)) {
				// 检查刷卡机的web状态是否需改为离线
				Log.println("刷卡机离线" + "仪器ID：" + IONode.this.instrumentID);
				this.stateForWeb = false;
				this.lastInstrumentState = 0;
			}

		}

		boolean getStateForUpdate() { // 适合同步返回true，否则返回false
			// 注：？？为减弱并发性可将触发更新需等待到心跳包个数设为非常数（可与仪器ID相关或一定范围的随机数）
			this.flush();
			if (this.numOfHeartBeat >= this.MaxHeartBeatNumForUpdate) {
				return true;
			} else {
				return false;
			}
		}

		boolean getStateForWeb() { // 在线返回true，离线返回false
			this.flush();
			return this.stateForWeb;
		}

		int getLastState() {
			return this.lastInstrumentState;
		}

		boolean isTimeOutForNet() { // 网络超时需关闭连接，这个函数经常调（频率高）
			long currentTime = System.currentTimeMillis();
			if (true == this.isRunning && currentTime - this.lastPackageTime > this.MaxNoPackageTimeForNet) {
				return true;
			} else {
				return false;
			}
		}
	}

	// 数据缓存与数据解析维护类（IO线程内部类）
	// // 暂存收到到字节数，调用解析函数解析数据包
	private class RecvByteBuffer {
		byte buffer[] = new byte[DataNodeBase.MaxDataByteSize * 5]; // 暂时采用单向队列存储接收到数据
		private int bufferHead = 0; // 待处理首字节位置
		int bufferTail = 0; // 待写入字节位置

		// 消息解析
		// 检查buffer，若有新消息则返回返回，若无完整数据返回null，同时修改起始指针至未处理到有效帧头
		DataRecv check() {
			// 处理数据：找帧头，校验消息，若消息没有问题，则取消息ID，校验内容，校验成功即可得消息结构体
			// 找帧头
			while (bufferTail - bufferHead >= DataNodeBase.MinDataByteSize) {
				// Log.println("接收缓存内容：：");
				// Log.printBytes(buffer, bufferHead, bufferTail);
				if (DataCheck.checkDataHead(buffer, bufferHead, bufferTail - bufferHead) == true) {
					break;
				}
				bufferHead++;
			}
			// 找到帧头，或数据太少不够一个最小包
			if (bufferTail - bufferHead >= DataNodeBase.MinDataByteSize) { // 找到帧头
				// Log.println("找到帧头，开始解析" + "--" + this.getClass().getName());
				int dataLength = DataCheck.checkDataLength(buffer, bufferHead, bufferTail - bufferHead);
				// 若长度不够则不修改bufferhead
				if (bufferTail - bufferHead >= dataLength) { // 剩余数据长度大于待内容检查消息长度
					// debug 显示收到到数据
					// Log.println("消息：");
					// Log.printBytes(buffer, bufferHead, bufferTail -
					// bufferHead);

					DataRecv newDataNode = DataCheck.check(buffer, bufferHead, dataLength);
					if (newDataNode != null) {
						bufferHead += dataLength;
						// Log.println("消息解析：有效包" + "--" +
						// this.getClass().getName());// debug

					} else {
						bufferHead++;
						Log.println("消息解析：无效包" + "仪器ID：" + IONode.this.instrumentID);// debug
					}
					return newDataNode;
				} else { // 包不完整
					this.clean();
					if (this.getFreeSize() <= 0) {// 包太大超过缓存（包不完整，同时clean后剩余空间仍为0）
						this.clear();
					}
				}
			}
			return null;
		}

		int getFreeSize() {
			return (buffer.length - bufferTail);
		}

		void clean() {// 清除已处理字节及无效字节，将帧头移到首字节
			if (bufferHead > 0) {
				for (int i = 0; i < bufferTail - bufferHead; i++) {
					buffer[i] = buffer[bufferHead + i];
				}
				bufferTail = bufferTail - bufferHead;
				bufferHead = 0;
			}
		}

		void clear() { // 清空缓存队列
			bufferHead = 0;
			bufferTail = 0;
		}
	}
}

// ** 待改进

// 2013-06-22 04:05:38 139 2 instrumentID:65540size > 0 && newDataNode == null size:1dataRecv.size():1dataRecv.peek():comm.DataHeartBeatRecv@9528c --comm.IONode.getRecvData--IONode.java:167

// 绑定流程
// // 刷卡机处于未初始化状态可请求仪器列表，绑定，解绑定。绑定时先请求绑定，通信服务根据仪器ID发送初始化列表，初始化列表发送完毕后发送初始化完成指令，
// // 刷卡机收到初始化完成指令后发送绑定确认，通信服务更新数据库中仪器绑定信息
// // 未初始化状态或已初始化状态下刷卡机都可以解绑定
// // 绑定和解绑定通信服务均不主动关闭连接，有刷卡机主动关闭连接或超时由通信服务关闭连接
// 可能存在的问题：刷卡机处于已初始化正常在线时，解绑定后，仍可正常收到同步消息？？
// 解绑定只在正常通信时用，解绑定之后不会再发同步消息？？
// // 目前对未绑定仪器在第一个心跳包处拦截，后期数据同步似乎没做限制。建议由刷卡机保证解绑定后关闭连接，重新连接以执行绑定操作，若由通信服务关闭连接需要保证刷卡机收到解绑定回复消息
// // ？？有没有必要初始化时处理解绑定消息


// ** 说明

// 未初始化状态或初始化状态若刷卡机端长时间无响应，通信服务（IO线程）均会主动关闭socket连接，时间上限为一定数量的心跳包时间（未初始化和初始化时限相同）（由配置文件设置）

//解决由于mySQL时间颗粒度过大照成到重复同步问题
//(A>=B && A+1000<NOW()) || A>=B+5000
////即A>=B &&(A+1000<NOW() || A-B>=5000) // 1S之后发或请求耽搁时间过长
//A-B>=5000发生的概率很大，导致A+1000<NOW()几乎不能执行
//改为(A>=B && A+1000<NOW())且A不能重复刷
//// ？？可能会有并发问题，标记待同步时有多个线程可能同时调用，多个web客户端

// 仍有IO线程收到消息过多，实际是由于积累心跳包问题可能是网络问题
// // 如果真的由于心跳包丢弃消息，不会有严重问题，照成的结果是同步不成功，造成重复同步
// // 可能会造成网络状态判断有误（积累的心跳包个数多，可以连续收到多个心跳包，但网络状态可能不好），添加最小心跳包时间，心跳包时间过短时不用于网络状态判断

// 收到包过多时不能清除当前所有包，处理最新的包（不能由IO线程删，IO仅能插队列，主循环线程仅能删队列）

// 重发问题
// 服务端负载过大时，对IO数据响应慢，可能引发多次重发（服务端反应慢时会导致刷卡机重发请求，不会照成严重问题）
// // 重发时是否有严重问题（出现到概率小）
// // 通信服务重发时：通信服务可能收到两条OK对一条同步，可能照成假成功，但假成功次数有限，问题是否严重，可能会丢数据标记同步成功了实际没有成功，
// // //
// 出现概率小，因回复消息没有标识不能避免多条OK照成丢同步的问题（出现这种情况是因为刷卡机端回复慢了，若由于网络原因最终会造成刷卡机离线，若由于刷卡机内部原因
// // // 已属于异常情况，造成的损失可通过申诉解决）
// // 刷卡机重发时：通信服务会收到两条相同同步请求，有可能同一条更新头重复同步时数据库返回异常（此时多条相同请求仅回一个OK，没有任何影响），
// // // 有可能重复同步不会有数据库异常（此时会回多条OK，造成的影响由刷卡机端处理）

// 关于消息队列使用linkedlist的安全性可能有问题？？
// // 暂用ConcurrentLinkedQueue（支持并发）

// 刷卡机状态管理：netstate中的stateForWeb通过bool值标记当前刷卡机是否在线，若为true表示在线；IONode成员变量
// lastInstrumentState记录最近一次刷卡机心跳包中的状态。作判断时应先看stateForWeb，若为false直接标记离线，若为true
// 则直接将lastInstrumentState发给web

// 若某种原因导致IO线程向客户端发送大量数据，客户端又没有接受，可能会造成socket缓冲满，阻塞write从而阻塞线程。此时在其他线程调用socket.close也不能
// 终止线程，除非客户端读走数据或关闭连接。本系统对此暂不做处理，实际中客户端肯定会有动作导致连接关闭，且不影响通信服务对其他客户端到处理

// 超时判断
// // 多个心跳包
// // 网络状态好指连续正常收到多个心跳包（心跳包有未正常到达的）
// // 长时间没有收到任何正确包则认为超时关闭连接
